package com.cea.papyrus.tabbedproperties.section;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.uml2.uml.OpaqueExpression;

import com.cea.papyrus.core.editpart.IUMLElementEditPart;
import com.cea.papyrus.core.tabbedproperties.PropertyViewSection;
import com.cea.papyrus.extensionpoints.editors.configuration.IDirectEditorConfiguration;
import com.cea.papyrus.extensionpoints.editors.utils.DirectEditorsUtil;
import com.cea.papyrus.tabbedproperties.composites.OpaqueExpressionBodiesComposite;
import com.cea.papyrus.umlutils.OpaqueExpressionUtil;

/**
 * Section for the Opaque Behavior bodies edition
 */
public class OpaqueExpressionBodiesSection extends PropertyViewSection {

	/** Composite where the body of an Opaque Behavior can be edited */
	private OpaqueExpressionBodiesComposite opaqueExpressionBodiesComposite;

	/** edition Area where the body is displayed and editable */
	protected SourceViewer editionArea;

	/** listener that listens to the language table */
	protected SelectionListener tableListener;

	/** Listener that listens to the focus lost in the source viewer */
	protected FocusListener editionAreaListener;

	/** Configuration for the source viewer that edits body language */
	protected IDirectEditorConfiguration configuration;

	/** current edited language */
	protected String language;
	
	/**
	 * {@inheritDoc}
	 */
	public void createControls(Composite parent, TabbedPropertySheetPage tabbedPropertySheetPage) {
		super.createControls(parent, tabbedPropertySheetPage);
		// set a grid Layout
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		layout.makeColumnsEqualWidth = true;
		layout.marginWidth = 0; // remove additional spaces
		layout.marginHeight = 0; // remove additional spaces
		parent.setLayout(layout);

		GridData data = new GridData();
		data.grabExcessHorizontalSpace = true;
		data.horizontalAlignment = SWT.FILL;

		// table for Parameters
		opaqueExpressionBodiesComposite = new OpaqueExpressionBodiesComposite();
		opaqueExpressionBodiesComposite.createContent(parent, tabbedPropertySheetPage.getWidgetFactory());
		opaqueExpressionBodiesComposite.setLayoutData(data);

		// edition area
		editionArea = new SourceViewer(parent, null, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
		editionArea.getControl().setLayoutData(data);
		editionArea.getTextWidget().setEditable(false);

		// Listener for edition
		final Table table = opaqueExpressionBodiesComposite.getTable();
		table.addSelectionListener(new SelectionListener() {

			public void widgetSelected(SelectionEvent e) {
				refreshSourceViewer();
			}

			public void widgetDefaultSelected(SelectionEvent e) {
			}
		});


		editionArea.getTextWidget().addFocusListener(new FocusListener() {
			public void focusGained(FocusEvent e){
				// check edition area was configured correctly
				if(editionArea.getDocument()==null) {
					refreshSourceViewer();
				}
			}
			public void focusLost(FocusEvent e){
				if((table.getSelection() != null) && (table.getSelection().length > 0)) {
					OpaqueExpression opaqueExpressionElement = (OpaqueExpression)opaqueExpressionBodiesComposite.getElement().getUml2Element();
					Object result = configuration.postEditAction(opaqueExpressionElement, editionArea.getDocument().get().trim());
					if(result==null) {
						OpaqueExpressionUtil.setBodyForLanguage(opaqueExpressionElement, getLanguage(), editionArea.getDocument().get().trim());
					}
				}
				refresh();
			}
		});
	}

	/**
	 * Configure the source viewer
	 * @param opaqueExpressionElement the Opaque Behavior to edit
	 */
	private void configureSourceViewer(OpaqueExpression opaqueExpressionElement, String text, String language) {
		editionArea.unconfigure();
		Document document = new Document(text);
		// getEditor for this language
		configuration = DirectEditorsUtil.findEditorConfiguration(language, "org.eclipse.uml2.uml.OpaqueExpression"); 
		configuration.setLanguage(language);
		editionArea.configure(configuration.getSourceViewerConfiguration());
		configuration.preEditAction(opaqueExpressionElement);
		editionArea.setDocument(document);
	}

	/**
	 * {@inheritDoc}
	 */
	public void refresh() {
		opaqueExpressionBodiesComposite.refresh();
		refreshSourceViewer();
	}

	/**
	 * Refresh source viewer, i.e. gives a new configuration 
	 */
	protected void refreshSourceViewer() {
		final Table table = opaqueExpressionBodiesComposite.getTable();
		if (editionArea!=null && !table.isDisposed() && !editionArea.getTextWidget().isDisposed()) {
			int selectionIndex = table.getSelectionIndex();
			if(selectionIndex != -1){ // selection not null
				// editionArea.getTextWidget().setEditable(true);
				OpaqueExpression opaqueExpressionElement = (OpaqueExpression)opaqueExpressionBodiesComposite.getElement().getUml2Element();
				setLanguage((String)table.getItem(selectionIndex).getData(OpaqueExpressionBodiesComposite.LANGUAGE));
				configureSourceViewer(opaqueExpressionElement, (String)(opaqueExpressionElement).getBodies().get(selectionIndex), getLanguage());
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void setInput(IWorkbenchPart part, ISelection selection) {
		super.setInput(part, selection);
		Assert.isTrue(selection instanceof IStructuredSelection);
		Object input = ((IStructuredSelection) selection).getFirstElement();
		if (input instanceof IUMLElementEditPart) {
			org.eclipse.uml2.uml.Element element = ((IUMLElementEditPart) input).getUmlElement();
			if(element instanceof org.eclipse.uml2.uml.OpaqueExpression) {
				com.cea.papyrus.umlutils.OpaqueExpression opaqueExpression = new com.cea.papyrus.umlutils.OpaqueExpression((org.eclipse.uml2.uml.OpaqueExpression)element);
				opaqueExpressionBodiesComposite.setElement(opaqueExpression);
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void dispose(){
		if(editionArea!=null && editionArea.getTextWidget()!=null && !editionArea.getTextWidget().isDisposed() && editionAreaListener!=null) {
			editionArea.getTextWidget().removeFocusListener(editionAreaListener);
			editionArea.unconfigure();
		}
		if(opaqueExpressionBodiesComposite!=null && opaqueExpressionBodiesComposite.getTable()!=null && !opaqueExpressionBodiesComposite.getTable().isDisposed() && tableListener!=null) { 
			opaqueExpressionBodiesComposite.getTable().removeSelectionListener(tableListener); 
		}
		if(opaqueExpressionBodiesComposite!=null) {
			opaqueExpressionBodiesComposite.dispose();
		}
		super.dispose();
	}

	/**
	 * @return the language
	 */
	public String getLanguage() {
		return language;
	}

	/**
	 * @param language the language to set
	 */
	public void setLanguage(String language) {
		this.language = language;
	}



}
